#ifndef __WIZ_FILE_DIRTREE_HPP__SHANHAOBO_19800429__
#define __WIZ_FILE_DIRTREE_HPP__SHANHAOBO_19800429__

#include "../../Core/Container/WizDataTypeLinkTree.hpp"
#include "../../DataType/WizDataTypeBoolean.hpp"

#include "../Archive/WizArchive.hpp"

#include "./WizFilePath.hpp"

namespace Wiz
{
    namespace File
    {
        namespace DirTree
        {
            namespace TraverseResult
            {
                enum Type
                {
                    eSuccess,
                    eFileNameConfict,
                    eDirAndFileNameConflict,
                    eNone,
                };
            }

            template<class DerivedT>
            struct Type : public ::Wiz::LinkTree::Type<DerivedT>
            {
                typedef ::Wiz::LinkTree::Type<tDerived>   tSuper;

                ::Wiz::UTF8::String::Type   m_Name;
                ::Wiz::Boolean8::Type       m_IsFile;
                Type(::Wiz::UTF8::String::In InName, ::Wiz::Bool::In InIsFile) : tSuper(), m_Name(InName), m_IsFile(InIsFile)
                {
                }

                Type() : tSuper()
                {
                }

                //////////////////////////////////////////////////////////////////////////
                tDerivedPtr InsertChild(::Wiz::UTF8::String::In InName, ::Wiz::Bool::In InIsFile)
                {
                    tDerivedPtr ChildPtr = new tDerived(InName, InIsFile);
                    if (::Wiz::IsValidPtr(ChildPtr))
                    {
                        tSuper::InsertChild(ChildPtr);
                    }

                    return ChildPtr;
                }
                tDerivedPtr InsertSibling(::Wiz::UTF8::String::In InName, ::Wiz::Bool::In InIsFile)
                {
                    tDerivedPtr SiblingPtr = new tDerived(InName, InIsFile);
                    if (::Wiz::IsValidPtr(SiblingPtr))
                    {
                        tSuper::InsertSibling(SiblingPtr);
                    }

                    return SiblingPtr;
                }
                //////////////////////////////////////////////////////////////////////////
                struct TraverseFuncBase_t : public TraverseFunc_t
                {
                    ::Wiz::File::Path::Type     m_FilePath;
                    ::Wiz::UInt::Type           m_CurrPos;
                    TraverseFuncBase_t(::Wiz::UTF8::String::In InPath, ::Wiz::Bool::In IsFile) : m_FilePath(InPath, IsFile), m_CurrPos(0)
                    {
                    }

                    virtual eTraverseTestResult Test(tOwnerPtr InDerivedPtr)
                    {
                        if (m_CurrPos < m_FilePath.DirectorySize())
                        {
                            if (InDerivedPtr->m_Name != m_FilePath[m_CurrPos])
                            {
                                return eSibling;
                            }
                        }
                        else
                        {
                            if (InDerivedPtr->m_Name != m_FilePath.FileNamePtr())
                            {
                                return eSibling;
                            }

                            return eSuccess;
                        }

                        m_CurrPos++;
                        if (m_CurrPos == m_FilePath.DirectorySize())
                        {
                            if (m_FilePath.IsFile() == ::Wiz::Bool::False)
                            {
                                return eSuccess;
                            }
                        }

                        if (InDerivedPtr->m_IsFile)
                        {
                            return eFailed;
                        }

                        return eChild;
                    }
                }; /// end of TraverseFuncBase_t
                //////////////////////////////////////////////////////////////////////////

                //////////////////////////////////////////////////////////////////////////
                struct FindTraverseFunc_t : public TraverseFuncBase_t
                {
                    typedef TraverseFuncBase_t  tSuper;
                    ::Wiz::File::DirTree::TraverseResult::Type  m_ResultReason;
                    tOwnerPtr                                   m_ResultPtr;
                    FindTraverseFunc_t(::Wiz::UTF8::String::In DirName, ::Wiz::Bool::In IsFile)
                        : tSuper(DirName, IsFile)
                        , m_ResultReason(::Wiz::File::DirTree::TraverseResult::eNone)
                        , m_ResultPtr(WIZ_NULLPTR)
                    {
                    }

                    virtual tOwnerPtr Success(tOwnerPtr InThisPtr)
                    {
                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eSuccess;
                        m_ResultPtr = InThisPtr;
                        return InThisPtr;
                    }

                    virtual tOwnerPtr Failed(tOwnerPtr InThisPtr)
                    {
                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eDirAndFileNameConflict;
                        m_ResultPtr = WIZ_NULLPTR;
                        return WIZ_NULLPTR;
                    }

                    virtual tOwnerPtr SiblingIsNull(tOwnerPtr InThisPtr)
                    {
                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eNone;
                        m_ResultPtr = WIZ_NULLPTR;
                        return WIZ_NULLPTR;
                    }
                    virtual tOwnerPtr ChildIsNull(tOwnerPtr InThisPtr)
                    {
                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eNone;
                        m_ResultPtr = WIZ_NULLPTR;
                        return WIZ_NULLPTR;
                    }
                };
                
                struct InsertTraverseFunc_t : public FindTraverseFunc_t
                {
                    InsertTraverseFunc_t(::Wiz::UTF8::String::In DirName, ::Wiz::Bool::In IsFile)
                        : FindTraverseFunc_t(DirName, IsFile)
                    {
                    }

                    virtual tOwnerPtr Success(tOwnerPtr InThisPtr)
                    {
                        m_ResultPtr = InThisPtr;
                        if (::Wiz::IsValidPtr(m_FilePath.FileNamePtr()))
                        {
                            m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eFileNameConfict;
                        }
                        else 
                        {
                            m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eSuccess;
                        }
                        return InThisPtr;
                    }

                    virtual tOwnerPtr Failed(tOwnerPtr InThisPtr)
                    {
                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eDirAndFileNameConflict;
                        m_ResultPtr = WIZ_NULLPTR;
                        return WIZ_NULLPTR;
                    }

                    virtual tOwnerPtr SiblingIsNull(tOwnerPtr InThisPtr)
                    {
                        if (::Wiz::IsValidPtr(m_FilePath.FileNamePtr()) && (m_CurrPos == m_FilePath.DirectorySize()))
                        {
                            return InThisPtr->InsertSibling(m_FilePath.FileNamePtr(), ::Wiz::Bool::True);
                        }
                        tOwnerPtr TempPtr = WIZ_NULLPTR;
                        {
                            TempPtr = InThisPtr->InsertSibling(::Wiz::UTF8::String::Type(m_FilePath[m_CurrPos]), ::Wiz::Bool::False);
                            m_CurrPos++;
                        }
                        for (; m_CurrPos < m_FilePath.DirectorySize(); m_CurrPos++)
                        {
                            TempPtr = TempPtr->InsertChild(::Wiz::UTF8::String::Type(m_FilePath[m_CurrPos]), ::Wiz::Bool::False);
                        }
                        ::Wiz::UTF8::Char::Ptr FileNamePtr = m_FilePath.FileNamePtr();
                        if (::Wiz::IsValidPtr(FileNamePtr))
                        {
                            TempPtr = TempPtr->InsertChild(::Wiz::UTF8::String::Type(FileNamePtr), ::Wiz::Bool::True);
                        }

                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eSuccess;
                        m_ResultPtr = TempPtr;
                        return TempPtr;
                    }

                    virtual tOwnerPtr ChildIsNull(tOwnerPtr InThisPtr)
                    {
                        tOwnerPtr TempPtr = InThisPtr;
                        for (; m_CurrPos < m_FilePath.DirectorySize(); m_CurrPos++)
                        {
                            TempPtr = TempPtr->InsertChild(::Wiz::UTF8::String::Type(m_FilePath[m_CurrPos]), ::Wiz::Bool::False);
                        }
                        ::Wiz::UTF8::Char::Ptr FileNamePtr = m_FilePath.FileNamePtr();
                        if (::Wiz::IsValidPtr(FileNamePtr))
                        {
                            TempPtr = TempPtr->InsertChild(::Wiz::UTF8::String::Type(FileNamePtr), ::Wiz::Bool::True);
                        }

                        m_ResultReason = ::Wiz::File::DirTree::TraverseResult::eSuccess;
                        m_ResultPtr = TempPtr;
                        return TempPtr;
                    }
                }; /// end of InsertTraverseFunc_t
                //////////////////////////////////////////////////////////////////////////

                struct PrintTree_t : public ForEachFunc_t
                {
                    virtual ::Wiz::Void::Type Do(tDerivedPtr InThisPtr)
                    {
                        tDerivedPtr TempPtr = InThisPtr;
                        if (::Wiz::NotValidPtr(TempPtr))
                        {
                            return;
                        }
                        if (::Wiz::IsValidPtr(TempPtr->GetFirstChildPtr()))
                        {
                            return;
                        }

                        ::Wiz::UTF8::String::Type OutPath;
                        TempPtr = TempPtr->GetParentPtr();
                        while (::Wiz::IsValidPtr(TempPtr))
                        {
                            OutPath = ::Wiz::UTF8::String::Type(TempPtr->m_Name + (::Wiz::UTF8::Char::Type)('\\') + OutPath);
                            TempPtr = TempPtr->GetParentPtr();
                        }
                        TempPtr = InThisPtr;
                        OutPath += TempPtr->m_Name;
                        if (TempPtr->m_IsFile == ::Wiz::Bool::False)
                        {
                            OutPath += (::Wiz::UTF8::Char::Type)'\\';
                        }

                        ::Wiz::String::Type CPath;
                        OutPath.To(CPath);
                        printf("%s\n", CPath.GetCStr());
                    }
                };
                ::Wiz::Bool::Type PrintTree()
                {

                    ForEach(PrintTree_t());
                    return ::Wiz::Bool::True;
                }
                //////////////////////////////////////////////////////////////////////////
                ::Wiz::Bool::Type MakeDirectory(::Wiz::UTF8::String::In DirName)
                {
                    if (::Wiz::IsNull(this->m_ParentPtr) &&
                        ::Wiz::IsNull(this->m_FirstChildPtr) &&
                        ::Wiz::IsNull(this->m_NextSiblingPtr) &&
                        m_Name == ::Wiz::UTF8::String::Type()
                        )
                    {
                        ::Wiz::File::Path::Type DirPath(DirName, ::Wiz::Bool::False);
                        if (DirPath.DirectorySize() < 1)
                        {
                            return ::Wiz::Bool::False;
                        }
                        m_Name = DirPath[0];
                    }

                    InsertTraverseFunc_t ITF(DirName, ::Wiz::Bool::False);
                    this->Traverse(ITF);
                    return ITF.m_ResultReason == ::Wiz::File::DirTree::TraverseResult::eSuccess;
                }

                ::Wiz::Bool::Type InsertFile(::Wiz::UTF8::String::In FileName, tDerivedPtr* ResultPtr)
                {
                    if (::Wiz::IsNull(this->m_ParentPtr) &&
                        ::Wiz::IsNull(this->m_FirstChildPtr) &&
                        ::Wiz::IsNull(this->m_NextSiblingPtr) &&
                        m_Name == ::Wiz::UTF8::String::Type()
                        )
                    {
                        ::Wiz::File::Path::Type DirPath(FileName, ::Wiz::Bool::True);
                        if (DirPath.DirectorySize() < 1)
                        {
                            return ::Wiz::Bool::False;
                        }
                        m_Name = DirPath[0];
                    }

                    InsertTraverseFunc_t ITF(FileName, ::Wiz::Bool::True);
                    this->Traverse(ITF);
                    if (ITF.m_ResultReason == ::Wiz::File::DirTree::TraverseResult::eSuccess)
                    {
                        if (::Wiz::IsValidPtr(ResultPtr))
                        {
                            *ResultPtr = ITF.m_ResultPtr;
                        }
                        return ::Wiz::Bool::True;
                    }
                    if (::Wiz::IsValidPtr(ResultPtr))
                    {
                        if (ITF.m_ResultReason == ::Wiz::File::DirTree::TraverseResult::eFileNameConfict)
                        {
                            *ResultPtr = ITF.m_ResultPtr;
                        }
                        else
                        {
                            *ResultPtr = WIZ_NULLPTR;
                        }
                    }

                    return ::Wiz::Bool::False;
                }

                //////////////////////////////////////////////////////////////////////////
                tDerivedPtr FindFile(::Wiz::UTF8::String::In FileName)
                {
                    FindTraverseFunc_t ITF(FileName, ::Wiz::Bool::True);
                    this->Traverse(ITF);

                    return ITF.m_ResultPtr;
                }
                tDerivedPtr FindDirectory(::Wiz::UTF8::String::In DirName)
                {
                    FindTraverseFunc_t ITF(DirName, ::Wiz::Bool::False);
                    this->Traverse(ITF);

                    return ITF.m_ResultPtr;
                }
                //////////////////////////////////////////////////////////////////////////

                tDerivedPtr RemoveFile(::Wiz::UTF8::String::In FileName)
                {
                    tDerivedPtr FoundPtr = FindFile(FileName);
                    if (::Wiz::IsValidPtr(FoundPtr))
                    {
                        FoundPtr->DetachSelf();
                        return FoundPtr;
                    }
                    return WIZ_NULLPTR;
                }

                tDerivedPtr RemoveDirectory(::Wiz::UTF8::String::In FileName)
                {
                    tDerivedPtr FoundPtr = FindDirectory(FileName);
                    if (::Wiz::IsValidPtr(FoundPtr))
                    {
                        FoundPtr->DetachSelf();
                        return FoundPtr;
                    }
                    return WIZ_NULLPTR;
                }
                //////////////////////////////////////////////////////////////////////////
            };
        } /// end of namespace DirTree
    } /// end of namespace File
} /// end of namespace Wiz

#endif /*__WIZ_FILE_DIRTREE_HPP__SHANHAOBO_19800429__*/
